﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Dotway.WPF.Panels.Utilities;

namespace Dotway.WPF.Panels
{
    /// <summary>
    /// A panel that fits its children into its content and places them in a
    /// dynamic grid. It is assumed that all children have the same size.    
    /// </summary>
    public class DynamicWrapPanel : Panel
    {
        #region Constructors

        public DynamicWrapPanel()
        { }

        #endregion Constructors
        #region Methods

        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement child in InternalChildren)
            {
                child.Measure(availableSize);
            }

            // This panel should always fit into the given size so we 
            // don't have to return a desired size here.
            Size size = availableSize;
            return size;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            if (InternalChildren.Count == 0)
            {
                return finalSize;
            }

            if (finalSize.Width == 0)
            {
                finalSize.Width = 100;
            }

            if (finalSize.Height == 0)
            {
                finalSize.Height = 100;
            }

            Size childSize = EstimateChildSize(finalSize);

            // Ratio between container and child.
            Size ratio = new Size();
            ratio.Width = finalSize.Width / childSize.Width;
            ratio.Height = finalSize.Height / childSize.Height;

            // Find number of rows and columns.
            Pair<int, int> gridDimensions = CalculateGridDimensions(ratio);
            int rows = gridDimensions.X;
            int cols = gridDimensions.Y;

            Size maxSize = new Size();
            maxSize.Width = finalSize.Width / cols;
            maxSize.Height = finalSize.Height / rows;

            ratio.Width = maxSize.Width / childSize.Width;
            ratio.Height = maxSize.Height / childSize.Height;
            double finalRatio = (ratio.Width < ratio.Height) ? ratio.Width : ratio.Height;

            int currentRow = 1;
            int currentColumn = 1;
            for (int i = 0; i < InternalChildren.Count; i++)
            {
                UIElement child = InternalChildren[i];                

                Size size = new Size(childSize.Width, childSize.Height);
                Size scale = new Size(1.0, 1.0);
                if (finalRatio < 1)
                {
                    scale.Width = finalRatio;
                    scale.Height = finalRatio;
                }
                else
                {
                    size = new Size(childSize.Width * finalRatio, childSize.Height * finalRatio);
                }

                ScaleTransform scaleTransform = child.RenderTransform as ScaleTransform;
                if (scaleTransform == null)
                {
                    scaleTransform = new ScaleTransform();
                }

                scaleTransform.ScaleX = scale.Width;
                scaleTransform.ScaleY = scale.Height;

                Point position = new Point(maxSize.Width * (currentColumn - 1), maxSize.Height * (currentRow - 1));
                
                // Center the items.
                position.X += (maxSize.Width - (size.Width * scale.Width)) / 2.0;
                position.Y += (maxSize.Height - (size.Height * scale.Height)) / 2.0;                                            

                child.RenderTransform = scaleTransform;
                child.Arrange(new Rect(position, size));

                currentColumn++;
                if (currentColumn > cols)
                {
                    // New row, move back to column 1.
                    currentColumn = 1;
                    currentRow++;
                }

            }

            return finalSize;
        }

        private Size EstimateChildSize(Size conatinerSize)
        {
            Size childSize = new Size();
            childSize.Width = InternalChildren[0].DesiredSize.Width;
            if (childSize.Width == 0)
            {
                childSize.Width = conatinerSize.Width;
            }

            childSize.Height = InternalChildren[0].DesiredSize.Height;
            if (childSize.Height == 0)
            {
                childSize.Height = conatinerSize.Height;
            }

            return childSize;
        }

        private Pair<int, int> CalculateGridDimensions(Size gridRatio)
        {
            // Find number of rows and columns.
            Pair<int, int> dimension = new Pair<int, int>(1, 1);
            double relativeRatio = (gridRatio.Width < gridRatio.Height) ? gridRatio.Height / gridRatio.Width : gridRatio.Width / gridRatio.Height;

            if (gridRatio.Width < gridRatio.Height)
            {
                while ((dimension.X * dimension.Y) < InternalChildren.Count)
                {
                    dimension.X++;

                    if ((dimension.X * dimension.Y) >= InternalChildren.Count)
                    {
                        break;
                    }

                    if (((double)dimension.X / dimension.Y) >= relativeRatio)
                    {
                        dimension.Y++;
                    }
                }
            }
            else
            {
                while ((dimension.X * dimension.Y) < InternalChildren.Count)
                {
                    dimension.Y++;

                    if ((dimension.X * dimension.Y) >= InternalChildren.Count)
                    {
                        break;
                    }

                    if (((double)dimension.Y / dimension.X) >= relativeRatio)
                    {
                        dimension.X++;
                    }
                }
            }
            return dimension;
        }

        #endregion Methods
    }
}

